﻿using System;
using System.Numerics;
using UtilZ.Dotnet.Ex.Base;
using UtilZ.Dotnet.Ex.Model;

namespace UtilZ.Dotnet.Ex.Normalization
{
#pragma warning disable 8500

    /// <summary>
    /// 线性归一化器
    /// </summary>
    unsafe public class Normalizer<TSource, TResult>
        where TSource : struct, IComparable<TSource>
#if CORE
        where TResult : struct, IComparable<TResult>, INumberBase<TResult>
#else
        where TResult : struct, IComparable<TResult>
#endif
    {
        private readonly IInnerNormalizer<TSource, TResult> _normalizer;

        /// <summary>
        /// 获取归一化选项参数
        /// </summary>
        public NormalizerOptions Options
        {
            get { return this._normalizer.Options; }
        }



        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="normalizer">归一化实例</param>
        public Normalizer(IInnerNormalizer<TSource, TResult> normalizer)
        {
            this._normalizer = normalizer;
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="options">归一化选项参数</param>
        /// <exception cref="NotImplementedException">不支持归一化数据类型</exception>
        public Normalizer(NormalizerOptions options)
        {
            if (options == null)
            {
                throw new ArgumentNullException(nameof(options));
            }

            PrimitiveDataTypes primitiveDataType = TypeEx.GetPrimitiveDataType(default(TSource));
            object innerNormalizer;
            var useMap = options.UseMap && options.HasInputRange();

            switch (primitiveDataType)
            {
                case PrimitiveDataTypes.Char:
                    innerNormalizer = new InnerNormalizer_Char<TResult>(options, useMap);
                    break;
                case PrimitiveDataTypes.SByte:
                    innerNormalizer = new InnerNormalizer_SByte<TResult>(options, useMap);
                    break;
                case PrimitiveDataTypes.Byte:
                    innerNormalizer = new InnerNormalizer_Byte<TResult>(options, useMap);
                    break;
                case PrimitiveDataTypes.Int16:
                    innerNormalizer = new InnerNormalizer_Int16<TResult>(options, useMap);
                    break;
                case PrimitiveDataTypes.UInt16:
                    innerNormalizer = new InnerNormalizer_UInt16<TResult>(options, useMap);
                    break;
                case PrimitiveDataTypes.Int32:
                    innerNormalizer = new InnerNormalizer_Int32<TResult>(options, useMap);
                    break;
                case PrimitiveDataTypes.UInt32:
                    innerNormalizer = new InnerNormalizer_UInt32<TResult>(options, useMap);
                    break;
                case PrimitiveDataTypes.Int64:
                    innerNormalizer = new InnerNormalizer_Int64<TResult>(options, useMap);
                    break;
                case PrimitiveDataTypes.UInt64:
                    innerNormalizer = new InnerNormalizer_UInt64<TResult>(options, useMap);
                    break;
                case PrimitiveDataTypes.Float:
                    innerNormalizer = new InnerNormalizer_Single<TResult>(options);
                    break;
                case PrimitiveDataTypes.Double:
                    innerNormalizer = new InnerNormalizer_Double<TResult>(options);
                    break;
#if CORE
                case PrimitiveDataTypes.Half:
                    innerNormalizer = new InnerNormalizer_Half<TResult>(options);
                    break;
#endif
                default:
                    throw new NotImplementedException($"未实现的归一化数据类型{nameof(TSource)}");
            }

            this._normalizer = (IInnerNormalizer<TSource, TResult>)innerNormalizer;
        }



        /// <summary>
        /// 重置归一化器,恢复至构造初始状态
        /// </summary>
        public void Reset()
        {
            this._normalizer.Reset();
        }

        /// <summary>
        /// 计算归一化系数
        /// </summary>
        /// <param name="minValue">数据源数据中的最小值</param>
        /// <param name="maxValue">数据源数据中的最大值</param>
        /// <param name="inputOffset">输入数据偏移量</param>
        /// <returns>归一化系数</returns>
        public double CaculateNormalizeFactor(double minValue, double maxValue, out double inputOffset)
        {
            return this._normalizer.CaculateNormalizeFactor(minValue, maxValue, out inputOffset);
        }

        /// <summary>
        /// 执行单个值归一化
        /// </summary>
        /// <param name="source">归一化原始数据</param>
        /// <param name="factor">归一化系数,当为映射时此参数不使用,填任意值</param>
        /// <param name="inputOffset">输入数据偏移量,当为映射时此参数不使用,填任意值</param>
        /// <returns>单个值归一化结果</returns>
        public TResult Excute(TSource source, double factor, double inputOffset)
        {
            return this._normalizer.Excute(source, factor, inputOffset);
        }

        /// <summary>
        /// 执行归一化,当原始数据长度与结果数据不一致时,取较小值为长度
        /// </summary>
        /// <param name="source">归一化原始数据</param>
        /// <param name="dest">归一化结果数据</param>
        public void Excute(Span<TSource> source, Span<TResult> dest)
        {
            this._normalizer.Excute(source, dest);
        }

        /// <summary>
        /// 执行归一化,当原始数据长度与结果数据不一致时,取较小值为长度
        /// </summary>
        /// <param name="source">归一化原始数据</param>
        /// <param name="dest">归一化结果数据</param>
        public void Excute(SpanZ<TSource> source, SpanZ<TResult> dest)
        {
            this._normalizer.Excute(source, dest);
        }

    }


    /*
    /// <summary>
    /// 归一化器
    /// </summary>
    unsafe public class Normalizer<T>
        where T : struct, INumberBase<T>, INumber<T>, IMultiplyOperators<T, T, T>, IDivisionOperators<T, T, T>
    {
        private readonly List<T> _slidFrame = null;

        private readonly int _slidFrames;
        /// <summary>
        /// 获取多帧之间的平滑帧数,大于0有效,小于等于0不做平滑
        /// </summary>
        public int SlidFrames
        {
            get { return this._slidFrames; }
        }


        private readonly T _min;
        /// <summary>
        /// 获取归一化最小值
        /// </summary>
        public T Min
        {
            get { return this._min; }
        }

        private readonly T _max;
        /// <summary>
        /// 获取归一化最大值
        /// </summary>
        public T Max
        {
            get { return this._max; }
        }

        private readonly T _range;
        private readonly T _offset;


        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="min">归一化最小值</param>
        /// <param name="max">归一化最大值</param>
        /// <param name="slidFrames">多帧之间的平滑帧数,大于0有效,小于等于0不做平滑</param>
        public Normalizer(T min, T max, int slidFrames)
        {
            if (slidFrames > 0)
            {
                if (this._slidFrame == null)
                {
                    this._slidFrame = new List<T>();
                }
            }
            this._slidFrames = slidFrames;
            this._range = (max - min) / T.CreateChecked(2);
            this._offset = max - this._range;

            this._min = min;
            this._max = max;
        }



        /// <summary>
        /// 重置所有归一化参数
        /// </summary>
        public void Reset()
        {
            if (this._slidFrame != null)
            {
                this._slidFrame.Clear();
            }
        }



        private T GetSourceAbsMax(MaxMinValueIndexInfo<T> minMax)
        {
            var dataAbsMax = T.Max(T.Abs(minMax.MinValue.Value), T.Abs(minMax.MaxValue.Value));
            if (this._slidFrame != null)
            {
                this._slidFrame.Add(dataAbsMax);
                while (this._slidFrame.Count > this._slidFrames)
                {
                    this._slidFrame.RemoveAt(0);
                }

                dataAbsMax = this._slidFrame.Max();
            }

            return dataAbsMax;
        }




        /// <summary>
        /// 执行short归一化
        /// </summary>
        /// <param name="source">归一化数据</param>
        public void Excute(Span<T> source)
        {
            var minMax = EnumerableEx.FindMinMaxIndex(source);
            var dataAbsMax = this.GetSourceAbsMax(minMax);
            T temp;
            for (int i = 0; i < source.Length; i++)
            {
                temp = (source[i] / dataAbsMax * this._range + this._offset);
                if (temp < this._min)
                {
                    temp = this._min;
                }
                else if (temp > this._max)
                {
                    temp = this._max;
                }

                source[i] = temp;
            }
        }

        /// <summary>
        /// 执行short归一化
        /// </summary>
        /// <param name="source">归一化数据</param>
        public void Excute(SpanZ<T> source)
        {
            var minMax = EnumerableEx.FindMinMaxIndex(source);
            var dataAbsMax = this.GetSourceAbsMax(minMax);
            T temp;
            var ptr = source.DataPtr;

            for (int i = 0; i < source.Length; i++)
            {
                temp = (ptr[i] / dataAbsMax * this._range + this._offset);
                if (temp < this._min)
                {
                    temp = this._min;
                }
                else if (temp > this._max)
                {
                    temp = this._max;
                }

                ptr[i] = temp;
            }
        }




        /// <summary>
        /// 执行归一化,当原始数据长度与结果数据不一致时,取较小值为长度
        /// </summary>
        /// <param name="source">归一化原始数据</param>
        /// <param name="dest">归一化结果数据</param>
        public void Excute<TResult>(Span<T> source, Span<TResult> dest)
            where TResult : struct, INumberBase<TResult>, INumber<TResult>, IMultiplyOperators<TResult, TResult, TResult>, IDivisionOperators<TResult, TResult, TResult>
        {
            int length = Math.Min(source.Length, dest.Length);
            var minMax = EnumerableEx.FindMinMaxIndex(source.Slice(0, length));
            var dataAbsMax = this.GetSourceAbsMax(minMax);
            T temp;

            for (int i = 0; i < length; i++)
            {
                temp = source[i] / dataAbsMax * this._range + this._offset;
                if (temp < this._min)
                {
                    temp = this._min;
                }
                else if (temp > this._max)
                {
                    temp = this._max;
                }

                dest[i] = TResult.CreateChecked(temp);
            }
        }

        /// <summary>
        /// 执行归一化,当原始数据长度与结果数据不一致时,取较小值为长度
        /// </summary>
        /// <param name="source">归一化原始数据</param>
        /// <param name="dest">归一化结果数据</param>
        public void Excute<TResult>(SpanZ<T> source, SpanZ<TResult> dest)
            where TResult : struct, INumberBase<TResult>, INumber<TResult>, IMultiplyOperators<TResult, TResult, TResult>, IDivisionOperators<TResult, TResult, TResult>
        {
            int length = Math.Min(source.Length, dest.Length);
            var minMax = EnumerableEx.FindMinMaxIndex(source.Slice(0, length));
            var dataAbsMax = this.GetSourceAbsMax(minMax);
            T* sourcePtr = source.DataPtr;
            TResult* destPtr = dest.DataPtr;
            T temp;

            for (int i = 0; i < length; i++)
            {
                temp = sourcePtr[i] / dataAbsMax * this._range + this._offset;
                if (temp < this._min)
                {
                    temp = this._min;
                }
                else if (temp > this._max)
                {
                    temp = this._max;
                }

                destPtr[i] = TResult.CreateChecked(temp);
            }
        }

    }
    */


}
